/*
 * ! Ext JS Library 3.1.1 Copyright(c) 2006-2010 Ext JS, LLC licensing@extjs.com
 * http://www.extjs.com/license
 */
Ext.ns('Ext.ux.grid');

Ext.ux.grid.ColumnHeaderGroup = Ext.extend(Ext.util.Observable, {

  constructor : function(config) {
    this.config = config;
  },

  init : function(grid) {
    Ext.applyIf(grid.colModel, this.config);
    Ext.apply(grid.getView(), this.viewConfig);
  },

  viewConfig : {
    initTemplates : function() {
      this.constructor.prototype.initTemplates.apply(this, arguments);
      var ts = this.templates || {};
      if (!ts.gcell) {
        ts.gcell = new Ext.XTemplate(
            '<td class="x-grid3-hd x-grid3-gcell x-grid3-td-{id} ux-grid-hd-group-row-{row} {cls}" style="{style}">',
            '<div {tooltip} class="x-grid3-hd-inner x-grid3-hd-{id}" unselectable="on" style="{istyle}">',
            this.grid.enableHdMenu ? '<a class="x-grid3-hd-btn" href="#"></a>' : '',
            '{value}</div></td>');
      }
      this.templates = ts;
      this.hrowRe = new RegExp("ux-grid-hd-group-row-(\\d+)", "");
    },

    renderHeaders : function() {
      var ts = this.templates, headers = [], cm = this.cm, rows = cm.rows, tstyle = 'width:'
          + this.getTotalWidth() + ';';

      for (var row = 0, rlen = rows.length; row < rlen; row++) {
        var r = rows[row], cells = [];
        for (var i = 0, gcol = 0, len = r.length; i < len; i++) {
          var group = r[i];
          group.colspan = group.colspan || 1;
          var id = this.getColumnId(group.dataIndex ? cm.findColumnIndex(group.dataIndex) : gcol), gs = Ext.ux.grid.ColumnHeaderGroup.prototype.getGroupStyle
              .call(this, group, gcol);
          cells[i] = ts.gcell.apply({
                cls : 'ux-grid-hd-group-cell',
                id : id,
                row : row,
                style : 'width:' + gs.width + ';' + (gs.hidden ? 'display:none;' : '')
                    + (group.align ? 'text-align:' + group.align + ';' : ''),
                tooltip : group.tooltip ? (Ext.QuickTips.isEnabled() ? 'ext:qtip' : 'title') + '="'
                    + group.tooltip + '"' : '',
                istyle : group.align == 'right' ? 'padding-right:16px' : '',
                btn : this.grid.enableHdMenu && group.header,
                value : group.header || '&nbsp;'
              });
          gcol += group.colspan;
        }
        headers[row] = ts.header.apply({
              tstyle : tstyle,
              cells : cells.join('')
            });
      }
      headers.push(this.constructor.prototype.renderHeaders.apply(this, arguments));
      return headers.join('');
    },

    onColumnWidthUpdated : function() {
      this.constructor.prototype.onColumnWidthUpdated.apply(this, arguments);
      Ext.ux.grid.ColumnHeaderGroup.prototype.updateGroupStyles.call(this);
    },

    onAllColumnWidthsUpdated : function() {
      this.constructor.prototype.onAllColumnWidthsUpdated.apply(this, arguments);
      Ext.ux.grid.ColumnHeaderGroup.prototype.updateGroupStyles.call(this);
    },

    onColumnHiddenUpdated : function() {
      this.constructor.prototype.onColumnHiddenUpdated.apply(this, arguments);
      Ext.ux.grid.ColumnHeaderGroup.prototype.updateGroupStyles.call(this);
    },

    getHeaderCell : function(index) {
      return this.mainHd.query(this.cellSelector)[index];
    },

    findHeaderCell : function(el) {
      return el ? this.fly(el).findParent('td.x-grid3-hd', this.cellSelectorDepth) : false;
    },

    findHeaderIndex : function(el) {
      var cell = this.findHeaderCell(el);
      return cell ? this.getCellIndex(cell) : false;
    },

    updateSortIcon : function(col, dir) {
      var sc = this.sortClasses, hds = this.mainHd.select(this.cellSelector).removeClass(sc);
      hds.item(col).addClass(sc[dir == "DESC" ? 1 : 0]);
    },

    handleHdDown : function(e, t) {
      var el = Ext.get(t);
      if (el.hasClass('x-grid3-hd-btn')) {
        e.stopEvent();
        var hd = this.findHeaderCell(t);
        Ext.fly(hd).addClass('x-grid3-hd-menu-open');
        var index = this.getCellIndex(hd);
        this.hdCtxIndex = index;
        var ms = this.hmenu.items, cm = this.cm;
        ms.get('asc').setDisabled(!cm.isSortable(index));
        ms.get('desc').setDisabled(!cm.isSortable(index));
        this.hmenu.on('hide', function() {
              Ext.fly(hd).removeClass('x-grid3-hd-menu-open');
            }, this, {
              single : true
            });
        this.hmenu.show(t, 'tl-bl?');
      } else if (el.hasClass('ux-grid-hd-group-cell') || Ext.fly(t).up('.ux-grid-hd-group-cell')) {
        e.stopEvent();
      }
    },

    handleHdMove : function(e, t) {
      var hd = this.findHeaderCell(this.activeHdRef);
      if (hd && !this.headersDisabled && !Ext.fly(hd).hasClass('ux-grid-hd-group-cell')) {
        var hw = this.splitHandleWidth || 5, r = this.activeHdRegion, x = e.getPageX(), ss = hd.style, cur = '';
        if (this.grid.enableColumnResize !== false) {
          if (x - r.left <= hw && this.cm.isResizable(this.activeHdIndex - 1)) {
            cur = Ext.isAir ? 'move' : Ext.isWebKit ? 'e-resize' : 'col-resize'; // col-resize
            // not
            // always
            // supported
          } else if (r.right - x <= (!this.activeHdBtn ? hw : 2)
              && this.cm.isResizable(this.activeHdIndex)) {
            cur = Ext.isAir ? 'move' : Ext.isWebKit ? 'w-resize' : 'col-resize';
          }
        }
        ss.cursor = cur;
      }
    },

    handleHdOver : function(e, t) {
      var hd = this.findHeaderCell(t);
      if (hd && !this.headersDisabled) {
        this.activeHdRef = t;
        this.activeHdIndex = this.getCellIndex(hd);
        var fly = this.fly(hd);
        this.activeHdRegion = fly.getRegion();
        if (!(this.cm.isMenuDisabled(this.activeHdIndex) || fly.hasClass('ux-grid-hd-group-cell'))) {
          fly.addClass('x-grid3-hd-over');
          this.activeHdBtn = fly.child('.x-grid3-hd-btn');
          if (this.activeHdBtn) {
            this.activeHdBtn.dom.style.height = (hd.firstChild.offsetHeight - 1) + 'px';
          }
        }
      }
    },

    handleHdOut : function(e, t) {
      var hd = this.findHeaderCell(t);
      if (hd && (!Ext.isIE || !e.within(hd, true))) {
        this.activeHdRef = null;
        this.fly(hd).removeClass('x-grid3-hd-over');
        hd.style.cursor = '';
      }
    },

    handleHdMenuClick : function(item) {
      var index = this.hdCtxIndex, cm = this.cm, ds = this.ds, id = item.getItemId();
      switch (id) {
        case 'asc' :
          ds.sort(cm.getDataIndex(index), 'ASC');
          break;
        case 'desc' :
          ds.sort(cm.getDataIndex(index), 'DESC');
          break;
        default :
          if (id.substr(0, 5) == 'group') {
            var i = id.split('-'), row = parseInt(i[1], 10), col = parseInt(i[2], 10), r = this.cm.rows[row], group, gcol = 0;
            for (var i = 0, len = r.length; i < len; i++) {
              group = r[i];
              if (col >= gcol && col < gcol + group.colspan) {
                break;
              }
              gcol += group.colspan;
            }
            if (item.checked) {
              var max = cm.getColumnsBy(this.isHideableColumn, this).length;
              for (var i = gcol, len = gcol + group.colspan; i < len; i++) {
                if (!cm.isHidden(i)) {
                  max--;
                }
              }
              if (max < 1) {
                this.onDenyColumnHide();
                return false;
              }
            }
            for (var i = gcol, len = gcol + group.colspan; i < len; i++) {
              if (cm.config[i].fixed !== true && cm.config[i].hideable !== false) {
                cm.setHidden(i, item.checked);
              }
            }
          } else {
            index = cm.getIndexById(id.substr(4));
            if (index != -1) {
              if (item.checked && cm.getColumnsBy(this.isHideableColumn, this).length <= 1) {
                this.onDenyColumnHide();
                return false;
              }
              cm.setHidden(index, item.checked);
            }
          }
          item.checked = !item.checked;
          if (item.menu) {
            var updateChildren = function(menu) {
              menu.items.each(function(childItem) {
                    if (!childItem.disabled) {
                      childItem.setChecked(item.checked, false);
                      if (childItem.menu) {
                        updateChildren(childItem.menu);
                      }
                    }
                  });
            }
            updateChildren(item.menu);
          }
          var parentMenu = item, parentItem;
          while (parentMenu = parentMenu.parentMenu) {
            if (!parentMenu.parentMenu
                || !(parentItem = parentMenu.parentMenu.items.get(parentMenu.getItemId()))
                || !parentItem.setChecked) {
              break;
            }
            var checked = parentMenu.items.findIndexBy(function(m) {
                  return m.checked;
                }) >= 0;
            parentItem.setChecked(checked, true);
          }
          item.checked = !item.checked;
      }
      return true;
    },

    beforeColMenuShow : function() {
      var cm = this.cm, rows = this.cm.rows;
      this.colMenu.removeAll();
      for (var col = 0, clen = cm.getColumnCount(); col < clen; col++) {
        var menu = this.colMenu, title = cm.getColumnHeader(col), text = [];
        if (cm.config[col].fixed !== true && cm.config[col].hideable !== false) {
          for (var row = 0, rlen = rows.length; row < rlen; row++) {
            var r = rows[row], group, gcol = 0;
            for (var i = 0, len = r.length; i < len; i++) {
              group = r[i];
              if (col >= gcol && col < gcol + group.colspan) {
                break;
              }
              gcol += group.colspan;
            }
            if (group && group.header) {
              if (cm.hierarchicalColMenu) {
                var gid = 'group-' + row + '-' + gcol;
                var item = menu.items.item(gid);
                var submenu = item ? item.menu : null;
                if (!submenu) {
                  submenu = new Ext.menu.Menu({
                        itemId : gid
                      });
                  submenu.on("itemclick", this.handleHdMenuClick, this);
                  var checked = false, disabled = true;
                  for (var c = gcol, lc = gcol + group.colspan; c < lc; c++) {
                    if (!cm.isHidden(c)) {
                      checked = true;
                    }
                    if (cm.config[c].hideable !== false) {
                      disabled = false;
                    }
                  }
                  menu.add({
                        itemId : gid,
                        text : group.header,
                        menu : submenu,
                        hideOnClick : false,
                        checked : checked,
                        disabled : disabled
                      });
                }
                menu = submenu;
              } else {
                text.push(group.header);
              }
            }
          }
          text.push(title);
          menu.add(new Ext.menu.CheckItem({
                itemId : "col-" + cm.getColumnId(col),
                text : text.join(' '),
                checked : !cm.isHidden(col),
                hideOnClick : false,
                disabled : cm.config[col].hideable === false
              }));
        }
      }
    },

    renderUI : function() {
      this.constructor.prototype.renderUI.apply(this, arguments);
      Ext.apply(this.columnDrop, Ext.ux.grid.ColumnHeaderGroup.prototype.columnDropConfig);
      Ext.apply(this.splitZone, Ext.ux.grid.ColumnHeaderGroup.prototype.splitZoneConfig);
    }
  },

  splitZoneConfig : {
    allowHeaderDrag : function(e) {
      return !e.getTarget(null, null, true).hasClass('ux-grid-hd-group-cell');
    }
  },

  columnDropConfig : {
    getTargetFromEvent : function(e) {
      var t = Ext.lib.Event.getTarget(e);
      return this.view.findHeaderCell(t);
    },

    positionIndicator : function(h, n, e) {
      var data = Ext.ux.grid.ColumnHeaderGroup.prototype.getDragDropData.call(this, h, n, e);
      if (data === false) {
        return false;
      }
      var px = data.px + this.proxyOffsets[0];
      this.proxyTop.setLeftTop(px, data.r.top + this.proxyOffsets[1]);
      this.proxyTop.show();
      this.proxyBottom.setLeftTop(px, data.r.bottom);
      this.proxyBottom.show();
      return data.pt;
    },

    onNodeDrop : function(n, dd, e, data) {
      var h = data.header;
      if (h != n) {
        var d = Ext.ux.grid.ColumnHeaderGroup.prototype.getDragDropData.call(this, h, n, e);
        if (d === false) {
          return false;
        }
        var cm = this.grid.colModel, right = d.oldIndex < d.newIndex, rows = cm.rows;
        for (var row = d.row, rlen = rows.length; row < rlen; row++) {
          var r = rows[row], len = r.length, fromIx = 0, span = 1, toIx = len;
          for (var i = 0, gcol = 0; i < len; i++) {
            var group = r[i];
            if (d.oldIndex >= gcol && d.oldIndex < gcol + group.colspan) {
              fromIx = i;
            }
            if (d.oldIndex + d.colspan - 1 >= gcol
                && d.oldIndex + d.colspan - 1 < gcol + group.colspan) {
              span = i - fromIx + 1;
            }
            if (d.newIndex >= gcol && d.newIndex < gcol + group.colspan) {
              toIx = i;
            }
            gcol += group.colspan;
          }
          var groups = r.splice(fromIx, span);
          rows[row] = r.splice(0, toIx - (right ? span : 0)).concat(groups).concat(r);
        }
        for (var c = 0; c < d.colspan; c++) {
          var oldIx = d.oldIndex + (right ? 0 : c), newIx = d.newIndex + (right ? -1 : c);
          cm.moveColumn(oldIx, newIx);
          this.grid.fireEvent("columnmove", oldIx, newIx);
        }
        return true;
      }
      return false;
    }
  },

  getGroupStyle : function(group, gcol) {
    var width = 0, hidden = true;
    for (var i = gcol, len = gcol + group.colspan; i < len; i++) {
      if (!this.cm.isHidden(i)) {
        var cw = this.cm.getColumnWidth(i);
        if (typeof cw == 'number') {
          width += cw;
        }
        hidden = false;
      }
    }
    return {
      width : (Ext.isBorderBox || (Ext.isWebKit && !Ext.isSafari2) ? width : Math.max(width
              - this.borderWidth, 0))
          + 'px',
      hidden : hidden
    };
  },

  updateGroupStyles : function(col) {
    var tables = this.mainHd.query('.x-grid3-header-offset > table'), tw = this.getTotalWidth(), rows = this.cm.rows;
    for (var row = 0; row < tables.length; row++) {
      tables[row].style.width = tw;
      if (row < rows.length) {
        var cells = tables[row].firstChild.firstChild.childNodes;
        for (var i = 0, gcol = 0; i < cells.length; i++) {
          var group = rows[row][i];
          if ((typeof col != 'number') || (col >= gcol && col < gcol + group.colspan)) {
            var gs = Ext.ux.grid.ColumnHeaderGroup.prototype.getGroupStyle.call(this, group, gcol);
            cells[i].style.width = gs.width;
            cells[i].style.display = gs.hidden ? 'none' : '';
          }
          gcol += group.colspan;
        }
      }
    }
  },

  getGroupRowIndex : function(el) {
    if (el) {
      var m = el.className.match(this.hrowRe);
      if (m && m[1]) {
        return parseInt(m[1], 10);
      }
    }
    return this.cm.rows.length;
  },

  getGroupSpan : function(row, col) {
    if (row < 0) {
      return {
        col : 0,
        colspan : this.cm.getColumnCount()
      };
    }
    var r = this.cm.rows[row];
    if (r) {
      for (var i = 0, gcol = 0, len = r.length; i < len; i++) {
        var group = r[i];
        if (col >= gcol && col < gcol + group.colspan) {
          return {
            col : gcol,
            colspan : group.colspan
          };
        }
        gcol += group.colspan;
      }
      return {
        col : gcol,
        colspan : 0
      };
    }
    return {
      col : col,
      colspan : 1
    };
  },

  getDragDropData : function(h, n, e) {
    if (h.parentNode != n.parentNode) {
      return false;
    }
    var cm = this.grid.colModel, x = Ext.lib.Event.getPageX(e), r = Ext.lib.Dom
        .getRegion(n.firstChild), px, pt;
    if ((r.right - x) <= (r.right - r.left) / 2) {
      px = r.right + this.view.borderWidth;
      pt = "after";
    } else {
      px = r.left;
      pt = "before";
    }
    var oldIndex = this.view.getCellIndex(h), newIndex = this.view.getCellIndex(n);
    if (cm.isFixed(newIndex)) {
      return false;
    }
    var row = Ext.ux.grid.ColumnHeaderGroup.prototype.getGroupRowIndex.call(this.view, h), oldGroup = Ext.ux.grid.ColumnHeaderGroup.prototype.getGroupSpan
        .call(this.view, row, oldIndex), newGroup = Ext.ux.grid.ColumnHeaderGroup.prototype.getGroupSpan
        .call(this.view, row, newIndex), oldIndex = oldGroup.col;
    newIndex = newGroup.col + (pt == "after" ? newGroup.colspan : 0);
    if (newIndex >= oldGroup.col && newIndex <= oldGroup.col + oldGroup.colspan) {
      return false;
    }
    var parentGroup = Ext.ux.grid.ColumnHeaderGroup.prototype.getGroupSpan.call(this.view, row - 1,
        oldIndex);
    if (newIndex < parentGroup.col || newIndex > parentGroup.col + parentGroup.colspan) {
      return false;
    }
    return {
      r : r,
      px : px,
      pt : pt,
      row : row,
      oldIndex : oldIndex,
      newIndex : newIndex,
      colspan : oldGroup.colspan
    };
  }
});